﻿import spade
from LBCrawlerAgent import LBCrawlerAgent, RespondLoadQuery
from CrawlerAgent import MyPeriodicBehaviour


_show_function_calls = True


  ##################################################################
  ## CentralLBCrawlerAgent                                        ##
  ##################################################################

class CentralLBCrawlerAgent(LBCrawlerAgent): # agent z consistent hashing i scentralizowanym LB
  class IfLeaderThenAskOthers(MyPeriodicBehaviour):
  #jesli jestem liderem, to pytam innych o qsize, upt
  # -> query-ref
    def _onTick(self):
      if _show_function_calls:
        print '\t+IfLeaderThenAskOthers'
      if self.myAgent.clb_agents == [] or not self.myAgent.is_leader():
        return
      self.myAgent.qsize_upt_sender = {}
      msg = spade.ACLMessage.ACLMessage()
      msg.setPerformative('query-ref')
      msg.setOntology(self.myAgent.service_name)
      msg.setContent('')
      for aname in self.myAgent.clb_agents:
        receiver = spade.AID.aid(name=aname, addresses=['xmpp://' + aname])
        msg.addReceiver(receiver)
##        print self.myAgent.getName(), '->', aname, msg
      print '\t\tSTART'
      self.myAgent.add_stat_sent_msgs()
      self.myAgent.send(msg)
      if _show_function_calls:
        print '\t-IfLeaderThenAskOthers'
  ##################################################################
  class ReceiveLoad(spade.Behaviour.Behaviour):
  #odbieram info o obciazeniu
  # <- inform
    def _process(self):
      msg = None
      msg = self._receive()
      if msg:
        self.myAgent.add_stat_rcvd_msgs()
        if _show_function_calls:
          print '\t+ReceiveLoad'
        rcvd_qsize, rcvd_upt = map(float, msg.getContent().split('#'))
        sender = msg.getSender()
        self.myAgent.qsize_upt_sender[(rcvd_qsize, rcvd_upt)] = sender.getName()
        if _show_function_calls:
          print '\t-ReceiveLoad'
  ##################################################################
  class SendOrders(MyPeriodicBehaviour):
  #jesli dalej jestem liderem, to wysylam polecenia wyslania n do ag
  # -> request
    def cmp_load(self, p, q):
      return int(p[0]*q[1] - q[0]*p[1])

    def _onTick(self):
      if _show_function_calls:
        print '\t+SendOrders'
      if self.myAgent.clb_agents == [] or not self.myAgent.is_leader():
        return
      self.myAgent.qsize_upt_sender[(self.myAgent.url_queue.qsize(), self.myAgent.avg_urls_per_tick)] = self.myAgent.getName()
      asc_load = sorted(self.myAgent.qsize_upt_sender.keys(), cmp=self.cmp_load)
##      print asc_load
##      print self.myAgent.qsize_upt_sender
      for i in xrange(len(asc_load)/2): # b ----> a
        qsize_a, upt_a = asc_load[i]
        qsize_b, upt_b = asc_load[-1-i]
        x = int(min(-(qsize_a * upt_b - qsize_b * upt_a) / (upt_a + upt_b), qsize_b / 2))
        aname = self.myAgent.qsize_upt_sender[asc_load[i]]
        bname = self.myAgent.qsize_upt_sender[asc_load[-1-i]]
        print bname, qsize_b/upt_b, (qsize_b-x)/upt_b, '---->', aname, qsize_a/upt_a, (qsize_a+x)/upt_a
        msg = spade.ACLMessage.ACLMessage()
        msg.setPerformative('request')
        msg.setOntology(self.myAgent.service_name)
        msg.setContent(aname + '#' + str(x))
        receiver = spade.AID.aid(name=bname, addresses=['xmpp://' + bname])
        msg.addReceiver(receiver)
        self.myAgent.add_stat_sent_msgs()
        self.myAgent.send(msg)
      self.myAgent.qsize_upt_sender = {}
      print '\t\tKONIEC'
      if _show_function_calls:
        print '\t-SendOrders'
  ##################################################################
  class ExecuteOrder(spade.Behaviour.Behaviour):
  #zawsze wykonuje polecenia wyslania n do ag
  # <- request
  # -> inform@transfer
    def _process(self):
      msg = None
      msg = self._receive()
      if msg:
        self.myAgent.add_stat_rcvd_msgs()
        if _show_function_calls:
          print '\t+ExecuteOrder'
        aname, url_num = msg.getContent().split('#')
        url_num = int(url_num)
        receiver = spade.AID.aid(name=aname, addresses=['xmpp://' + aname])
        self.myAgent.send_urls(url_num, receiver)
        if _show_function_calls:
          print '\t-ExecuteOrder'
  ##################################################################
  def is_leader(self):
    my_name = self.getName()
    agents = [my_name] + self.clb_agents
    agents.sort()
    return my_name == agents[0]
  ##################################################################
  def set_clb_agents(self, agents):
    self.clb_agents = agents
    print '#clb', self.clb_agents
  ##################################################################
  def _setup(self):
    self.clb_agents = []
    self.qsize_upt_sender = {}

    self.service_name = 'central_lb'
    self.transfer_service_name = self.service_name + '_url_transfer'
    self.set_lb_agents = self.set_clb_agents

    b = self.IfLeaderThenAskOthers(self.behav_period, self.lb_timestart)
    self.addBehaviour(b)

    template = spade.Behaviour.ACLTemplate()
    template.setPerformative('inform')
    template.setOntology(self.service_name)
    mt = spade.Behaviour.MessageTemplate(template)
    b = self.ReceiveLoad()
    self.addBehaviour(b, mt)

    template = spade.Behaviour.ACLTemplate()
    template.setPerformative('query-ref')
    template.setOntology(self.service_name)
    mt = spade.Behaviour.MessageTemplate(template)
    b = RespondLoadQuery()
    self.addBehaviour(b, mt)

    b = self.SendOrders(self.behav_period, self.lb_timestart + 3)
    self.addBehaviour(b)

    template = spade.Behaviour.ACLTemplate()
    template.setPerformative('request')
    template.setOntology(self.service_name)
    mt = spade.Behaviour.MessageTemplate(template)
    b = self.ExecuteOrder()
    self.addBehaviour(b, mt)

    LBCrawlerAgent._setup(self)
